import { EventType } from "../event/index";
import { ComponentType } from "../component/index";
import { must, any, has } from "../core/logic";
import { proj } from "../core/crs/proj";
import { Engine } from "../Engine";
import { Mesh } from "../component/index";
import { getImage } from "../core/ajax";
import { Geometry } from "../geom/index";
import { BasicMaterial, StandardMaterial } from "../material/index";
import { Object3D } from "./Object3D";
import { Base } from "../core/Base";
class TMS extends Base {
    constructor(opts = {}) {
        super(opts);
        this.type = "TMS";
        this.on(EventType.ComponentEvent, event => {
            if (event.componentType !== ComponentType.Camera) {
                return;
            }
            this.extentChange();
        });
        this.minZoom = any(opts.minZoom, 0);
        this.maxZoom = any(opts.maxZoom, 20);
        this.url = must(opts.url);//切片地址
        this.size = any(opts.size, 256);//切片大小
        this.origin = opts.origin;
        this.resolutions = any(opts.resolutions, []);
        this.crs = any(opts.crs, "4326");
        this.expand=any(opts.expand,2);
        this.zoomOffset = any(opts.zoomOffset, 0);
        this.images = new Cache(opts);
        this.objects = new Map();
        this.nofounds=new Set();
        this.proxy=opts.proxy;
        this.extentChange();
    }
    createGeometry(position, resolution, geometry) {
        if (!geometry) {
            geometry = new Geometry();
            geometry.texCood0 = new Float32Array(
                [
                    0 + 1 / this.size, 0 + 1 / this.size,
                    0 + 1 / this.size, 1 - 1 / this.size,
                    1 - 1 / this.size, 1 - 1 / this.size,
                    1 - 1 / this.size, 0 + 1 / this.size
                ]);
            geometry.normal = new Float32Array(
                [
                    0, 0, 1,
                    0, 0, 1,
                    0, 0, 1,
                    0, 0, 1
                ]);
            geometry.indices = new Uint32Array([0, 1, 2, 0, 2, 3]);
        }
        let size = resolution * (this.size);
        geometry.position = new Float32Array(
            [
                position[0]-Engine.instance.center[0], position[1]-Engine.instance.center[1], 0,
                position[0]-Engine.instance.center[0], position[1]-Engine.instance.center[1] - size, 0,
                position[0]-Engine.instance.center[0] + size, position[1]-Engine.instance.center[1] - size, 0,
                position[0]-Engine.instance.center[0] + size, position[1]-Engine.instance.center[1], 0,
            ]);
        return geometry;
    }
    updateTile(xyz) {

    }
    set origin(v) {
        this._origin = v;
        return this;
    }
    get origin() {
        return this._origin;
    }
    set minZoom(v) {
        this._minZoom = v;
        return this;
    }
    get minZoom() {
        return this._minZoom;
    }
    set maxZoom(v) {
        this._maxZoom = v;
        return this;
    }
    get maxZoom() {
        return this._maxZoom;
    }
    set resolutions(v) {
        this._resolutions = v;
        return this;
    }
    get resolutions() {
        return this._resolutions;
    }
    get units() {
        return proj.defs(this.crs).units;
    }
    set crs(v) {
        this._crs = v;
        if (this.resolutions.length < 1) {
            //构建分辨率
            let res = [];
            let units = this.units;
            if (units === "degrees") {
                let base = 180 * 2 / this.size;
                for (let i = 0; i < this.maxZoom; i++) {
                    res.push(base / Math.pow(2, i));
                }
            } else if (units === "m") {
                let base = 20037508.3427892 * 2 / this.size;
                for (let i = 0; i < this.maxZoom; i++) {
                    res.push(base / Math.pow(2, i));
                }
            } else {
                console.error("不支持的图层坐标系");
            }
            this.resolutions = res;
        }
        if (!has(this.origin)) {
            let units = this.units;
            if (units === "degrees") {
                this.origin = [-180, 90];
            } else {
                this.origin = [-20037508.3427892, 20037508.3427892];
            }
        }
        return this;
    }
    get crs() {
        return this._crs;
    }
    set url(v) {
        this._url = v;
        return this;
    }
    get url() {
        return this._url;
    }
    set size(v) {
        this._size = v;
        return this;
    }
    get size() {
        return this._size;
    }
    async getTiles() {
        this.needRender = false;
        this.lock = true;
        let { zoom } = this.getZoom();
        zoom = Math.floor(zoom);//整数级别锚定
        let resolution = this.resolutions[zoom];
        let bound = Engine.instance.scene.getExtent(this.expand);
        let minCol = (bound[0] - this.origin[0]+Engine.instance.center[0]) / resolution;
        minCol = Math.floor(minCol / this.size);
        let maxCol = (bound[2] - this.origin[0]+Engine.instance.center[0]) / resolution;
        maxCol = Math.floor(maxCol / this.size) + 1;
        let minRow = (this.origin[1] - bound[3]-Engine.instance.center[1]) / resolution;
        minRow = Math.floor(minRow / this.size);
        let maxRow = (this.origin[1] - bound[1]-Engine.instance.center[1]) / resolution;
        maxRow = Math.floor(maxRow / this.size) + 1;

        let promises = [];
        for (let i = minCol; i <= maxCol; i++) {
            for (let j = minRow; j <= maxRow; j++) {
                let id = zoom + "-" + i + "-" + j;
                let image = this.images.get(id);
                if (!image) {
                    if(this.nofounds.has(id)){
                        continue;
                    }
                    let promise = this.getTile(i, j, zoom).then((bitmap) => {
                        if (bitmap) {
                            this.images.add(bitmap);
                        }else{
                            this.nofounds.add(id);
                        }
                    }).catch(() => {
                        this.nofounds.add(id);
                    });
                    promises.push(promise);
                } else {
                    this.images.delete(id);//移动到最新添加
                    this.images.add(image);
                }
            }
        }
        await Promise.all(promises);
        let list = [];//记录已经存在的
        let toAdd = [];//记录不存在的需要添加；
        for (let i = minCol; i <= maxCol; i++) {
            for (let j = minRow; j <= maxRow; j++) {
                let id = zoom + "-" + i + "-" + j;
                let tile = this.objects.get(id);
                if (tile) {
                    this.objects.delete(id);
                    list.push(tile);
                } else {
                    toAdd.push({ id, i, j, zoom });
                }
            }
        }
        let pool = this.objects.values();
        let cursor;
        for (let m = 0; m < toAdd.length; m++) {
            let id = toAdd[m].id, i = toAdd[m].i, j = toAdd[m].j;
            let x = this.origin[0] + resolution * this.size * i;
            let y = this.origin[1] - resolution * this.size * j;

            let position = [x, y];
            let map = this.images.get(id);
            if (!map) {
                continue;
            }
            cursor = pool.next();
            if (cursor.value) {
                this.objects.delete(cursor.value.tid);
                this.createGeometry(position, resolution, cursor.value.mesh.geometry);
                //更新纹理
                Engine.instance.device.queue.copyExternalImageToTexture(
                    { source: map },
                    { texture: cursor.value.mesh.material.map.texture},//, origin: [0, 0, index]
                    [this.size,this.size]
                );
                cursor.value.trans.scale = [1, 1, 1];
                cursor.value.tid = id;
                list.push(cursor.value);
            } else {
                let material = new BasicMaterial({mipMap:false, map, defuseLight: true,depthWriteEnabled:false});
                //构建几何体
                let geometry = this.createGeometry(position, resolution);
                let mesh = new Mesh({ id, geometry, material });
                let object = new Object3D();
                object.addComponent(mesh);
                Engine.instance.scene.addEntity(object);
                object.tid = id;
                list.push(object);
            }
        }
        this.objects.forEach((value,key)=>{
            value.trans.scale = [0, 0, 0];
            // value.mesh.material.color="0x000000";
        })
        //还回去
        for (let i = 0; i < list.length; i++) {
            list[i].trans.scale=[1,1,1];
            this.objects.set(list[i].tid, list[i]);
        }
        this.lock = false;
        if (this.needRender) {
            this.getTiles();
        }
    }
    async getTile(x, y, z) {
        let url = this.url.replace("{x}", x)
            .replace("{y}", y)
            .replace("{z}", z);
        if(this.proxy){
            url=this.proxy+"?originUrl="+encodeURIComponent(url);
        }
        let image = await getImage(url);
        image.id = z + "-" + x + "-" + y;
        return image;
    }

    //根据相机的距离计算图层级别
    getZoom() {
        let camera = Engine.instance.scene.getCamera();
        let zoom = 0;
        let distance = camera.distance;
        let res = distance * Math.tan(camera.fovy / 2) * 2 / camera.size[1];
        if (res > this.resolutions[0]) {
            zoom = 0;
        } else if (res < this.resolutions[this.resolutions.length - 1]) {
            zoom = this.resolutions.length - 1;
        } else {
            for (let i = 0; i < this.resolutions.length - 1; i++) {
                let pre = this.resolutions[i], next = this.resolutions[i + 1];
                if (res <= pre && res >= next) {
                    zoom = i + (pre - res) / (pre - next)
                    break;
                }
            }
        }
        return { zoom };
    }

    extentChange() {
        if (this.lock) {
            this.needRender = true;
            return;
        }
        this.getTiles();
    }
}
class Cache extends Map {
    constructor(opts = {}) {
        super();
        this.maxSize = any(opts.maxSize, 20000);
    }
    add(item) {
        this.set(item.id, item);
        if (this.size > this.maxSize) {
            for (let [key, value] of this) {
                this.delete(key);
                break; // 找到第一个元素后退出循环
            }
        }
    }
}
export { TMS }
